package com.laicunba.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.laicunba.event.SellOutEvent;
import com.laicunba.pojo.CashCouponDO;
import com.laicunba.pojo.CurrentAccountItemDO;
import com.laicunba.pojo.FinancingTargetBidDO;
import com.laicunba.pojo.FinancingTargetDO;
import com.laicunba.pojo.RateIncrCouponDO;
import com.laicunba.pojo.UserBankDO;
import com.laicunba.pojo.UserDO;
import com.laicunba.service.AlertService;
import com.laicunba.service.CashCouponService;
import com.laicunba.service.CurrentAccountService;
import com.laicunba.service.FinancingTargetBidService;
import com.laicunba.service.FinancingTargetService;
import com.laicunba.service.RateIncrCouponService;
import com.laicunba.service.SaleService;
import com.laicunba.service.UserBankService;
import com.laicunba.service.UserService;
import com.laicunba.service.exception.MyException;
import com.laicunba.util.AssertUtil;
import com.laicunba.util.StringUtil;
import com.laicunba.util.ThreadUtil;

@Service
public class SaleServiceImpl implements SaleService, ApplicationEventPublisherAware {
	private static final Logger LOG = Logger.getLogger(SaleServiceImpl.class);
	@Autowired
	private UserService userService;
	@Autowired
	private FinancingTargetService financingTargetService;
	@Autowired
	private FinancingTargetBidService financingTargetBidService;
	@Autowired
	private UserBankService userBankService;
	@Autowired
	private CashCouponService cashCouponService;
	@Autowired
	private RateIncrCouponService rateIncrCouponService;
	@Autowired
	private CurrentAccountService currentAccountService;
	@Autowired
	private AlertService alertService;

	private ApplicationEventPublisher applicationEventPublisher;

	@Transactional
	@Override
	public synchronized boolean makeFinancingTargetBid(MakeFinancingTargetBidParam param) {
		// check parameters
		if (StringUtil.isEmpty(param.getBuyUserId()) || StringUtil.isEmpty(param.getCertificateId()) || StringUtil.isEmpty(param.getFinancingTargetId()) || param.getBuyAmount() == null) {
			AssertUtil.assertFail("PARAM_ERROR");
		}

		FinancingTargetDO financingTarget = financingTargetService.get(FinancingTargetDO.class, param.getFinancingTargetId());
		AssertUtil.assertNotNull(financingTarget, "FINANCING_TARGET_NOT_EXIST");
		AssertUtil.assertTrue("onsale".equalsIgnoreCase(financingTarget.getStatus()), "FINANCING_TARGET_NOT_ONSALE");
		AssertUtil.assertTrue(financingTarget.getCollectAmount() != null, "COLLECT_AMOUNT_ERROR");
		if (financingTarget.getFinishAmount() == null) {
			financingTarget.setFinishAmount(BigDecimal.ZERO);
		}
		AssertUtil.assertTrue(financingTarget.getCollectAmount().subtract(financingTarget.getFinishAmount()).compareTo(param.getBuyAmount()) >= 0, "COLLECT_AMOUNT_NOT_ENOUGH");

		AssertUtil.assertTrue((financingTarget.getLimitAmount() == null) || financingTarget.getLimitAmount().compareTo(BigDecimal.ZERO) == 0 || financingTarget.getLimitAmount().compareTo(param.getBuyAmount()) >= 0, "MORE_THEN_LIMIT");

		UserDO buyer = userService.get(UserDO.class, param.getBuyUserId());
		AssertUtil.assertNotNull(buyer, "BUYER_NOT_EXIST");

		AssertUtil.assertTrue(financingTarget.getBeginDate()!=null&&financingTarget.getBeginDate().getTime()<System.currentTimeMillis(),"NOT_BEGIN_SALE");
		
		// add finish amount
		if (param.isNeedLockAmount()) {
			AssertUtil.assertTrue(financingTargetService.subtractStock(financingTarget.getId(), param.getBuyAmount()), "FINANCING_TARGET_SUBTRACT_STOCK_ERROR");
		}

		// bid record
		FinancingTargetBidDO bidDO = new FinancingTargetBidDO();
		bidDO.setBidUserId(param.getBuyUserId());
		bidDO.setBuyAmount(param.getBuyAmount());
		bidDO.setCertificateId(param.getCertificateId());
		bidDO.setPay(false);
		bidDO.setLockAmount(param.isNeedLockAmount());
		bidDO.setSummary(param.getSummary());
		bidDO.setFinancingTargetId(param.getFinancingTargetId());
		bidDO.setBankCardNo(param.getBankCardNo());
		bidDO.setBankName(param.getBankName());
		bidDO.setPayPid(param.getPayPid());
		bidDO.setPayName(param.getPayName());
		bidDO.setBankNo(param.getBankNo());
		bidDO.setAnnualizedRates(financingTarget.getAnnualizedRates());
		bidDO.setWithdrawPrincipal(BigDecimal.ZERO);
		bidDO.setWithdrawProfit(BigDecimal.ZERO);
		bidDO.setType(financingTarget.getType());
		bidDO.setDeductionProfit(BigDecimal.ZERO);
		bidDO.setWithdrawType(param.getWithdrawType());
		bidDO.setChannel(param.getChannel());
		bidDO.setCashCouponId(param.getCashCouponId());
		bidDO.setTransferCurrent(param.isTransferCurrent());
		bidDO.setRateIncrCouponId(param.getRateIncrCouponId());
		bidDO.setPayChannel(param.getPayChannel());
		bidDO.setSeq(param.getSeq());
		// 检查现金券
		if (StringUtil.isNotEmpty(param.getCashCouponId())) {
			CashCouponDO cashCouponDO = cashCouponService.get(CashCouponDO.class, param.getCashCouponId());

			// 检查现金券条件
			AssertUtil.assertTrue(cashCouponService.testForConditions(bidDO, cashCouponDO, financingTarget));
		}

		// 检查加息券
		if (StringUtil.isNotEmpty(param.getRateIncrCouponId())) {
			RateIncrCouponDO rateIncrCouponDO = rateIncrCouponService.get(RateIncrCouponDO.class, param.getRateIncrCouponId());
			// 检查现加息条件
			AssertUtil.assertTrue(rateIncrCouponService.testForConditions(bidDO, rateIncrCouponDO, financingTarget));
		}

		AssertUtil.assertTrue(financingTargetBidService.create(bidDO), "CREATE_FINANCING_TARGET_BID_ERROR");
		// 允许超卖,所以不需要下面代码的检查
		// AssertUtil.assertTrue(financingTargetService.checkBidRecordIsRight(param.getFinancingTargetId()),
		// "FINANCING_TARGET_ERROR");
		return true;
	}

	@Transactional
	@Override
	public boolean payedFinancingTarget(PayedFinancingTargetParam payedParam) {
		if (StringUtil.isEmpty(payedParam.getCertificateId()) || StringUtil.isEmpty(payedParam.getPayMark())) {
			AssertUtil.assertFail("PARAM_ERROR");
		}
		List<FinancingTargetBidDO> bidList = financingTargetBidService.findByCertificateId(payedParam.getCertificateId());
		AssertUtil.assertTrue(bidList != null && !bidList.isEmpty(), "FINANCING_TARGET_BID_NOT_EXIST");
		for (FinancingTargetBidDO bidDO : bidList) {
			AssertUtil.assertFalse(bidDO.isPay(), "FINANCING_TARGET_BID_HAS_PAYED");
			bidDO.setPayMark(payedParam.getPayMark());
			bidDO.setPay(true);
			bidDO.setPayTime(new Date());
			FinancingTargetDO financingTargetDO = financingTargetService.get(FinancingTargetDO.class, bidDO.getFinancingTargetId());
			AssertUtil.assertNotNull(financingTargetDO);
			// bidDO.setBeginProfit(financingTargetService.getFirstProfitDate(financingTargetDO.getProfitFlag(),
			// new Date()));
			Date beginDate = new Date();

			if (payedParam.gettN() == -1) {
				if ("scb".equalsIgnoreCase(bidDO.getType())) {
					beginDate.setDate(beginDate.getDate() + 1);
				}
			} else {
				beginDate.setDate(beginDate.getDate() + payedParam.gettN());
			}

			beginDate.setHours(0);
			beginDate.setMinutes(0);
			beginDate.setSeconds(0);
			bidDO.setBeginProfit(beginDate);

			Date endProfit = new Date(bidDO.getBeginProfit().getTime());
			endProfit.setDate(endProfit.getDate() + financingTargetDO.getPeriod());
			bidDO.setEndProfit(endProfit);
			AssertUtil.assertTrue(financingTargetBidService.updateSkipCheckOwner(bidDO), "UPDATE_FINANCING_TARGET_BID_ERROR");
			if (!bidDO.isLockAmount()) {
				// 如果之前没有锁定库存,现在就去减少
				AssertUtil.assertTrue(financingTargetService.subtractStock(bidDO.getFinancingTargetId(), bidDO.getBuyAmount()));
			}
			if (StringUtil.isNotEmpty(bidDO.getBankName(), bidDO.getBankCardNo(), bidDO.getPayName(), bidDO.getPayPid(), bidDO.getBankNo())) {
				if (userBankService.find(bidDO.getBidUserId(), bidDO.getBankCardNo()) == null) {
					UserBankDO userBank = new UserBankDO();
					userBank.setBankCardNo(bidDO.getBankCardNo());
					userBank.setBankCode(bidDO.getBankNo());
					userBank.setBankName(bidDO.getBankName());
					userBank.setHolder(bidDO.getPayName());
					userBank.setPid(bidDO.getPayPid());
					userBank.setUserId(bidDO.getBidUserId());
					AssertUtil.assertTrue(userBankService.create(userBank), "CREATE_USER_BANK_ERROR");
				}
			}
			// 处理现金券
			dealCashCoupon(bidDO, financingTargetDO);
			// 处理加息券
			dealRateIncrCoupon(bidDO, financingTargetDO);

			try {
				if (financingTargetDO.getCollectAmount().subtract(financingTargetDO.getFinishAmount() != null ? financingTargetDO.getFinishAmount() : BigDecimal.ZERO).subtract(bidDO.getBuyAmount()).compareTo(BigDecimal.valueOf(1000)) < 0) {
					// 库存小于1000发预警信号
					final String targetId = financingTargetDO.getId();
					ThreadUtil.execute(new Runnable() {

						@Override
						public void run() {
							SellOutEvent event = new SellOutEvent(targetId);
							applicationEventPublisher.publishEvent(event);
						}
					});
				}
			} catch (Exception e) {
				alertService.error(null, String.format("%s天期的标的已售完,但无候选标,未自动上架成功!", financingTargetDO.getPeriod()));
				LOG.error("发出库存不足预警,处理出错", e);
			}
			// 如果是活期,并且该用户开通了复利,则走新的活期账户
			if ("scb".equalsIgnoreCase(bidDO.getType())) {
				UserDO userDO = userService.get(UserDO.class, bidDO.getBidUserId());
				if ("CURRENT_ACCOUNT".equalsIgnoreCase(userDO.gethType())) {
					putIntoCurrentAccount(userDO, bidDO);
				}
			}
		}
		return true;
	}

	/**
	 * 把活期
	 * 
	 * @param userDO
	 * @param bidDO
	 */
	private void putIntoCurrentAccount(UserDO userDO, FinancingTargetBidDO bidDO) {
		boolean success = financingTargetBidService.frozen(bidDO.getId(), "转入复利活期账户");
		if (success) {
			LOG.info(String.format("tag:frozen-success,uid:%s,bid:%s,转复利冻结成功", userDO.getId(), bidDO.getId()));
			CurrentAccountItemDO currentAccountItemDO = currentAccountService.add(userDO.getId(), bidDO.getBuyAmount(), "购买", bidDO.getId());
			if (currentAccountItemDO != null) {
				LOG.info(String.format("tag:current_account_item-success,uid:%s,bid:%s,current_account_item_id:%s", userDO.getId(), bidDO.getId(), currentAccountItemDO.getId()));
			} else {
				alertService.warn(userDO.getId(), String.format("tag:current_account_item-fail,uid:%s,bid:%s", userDO.getId(), bidDO.getId()));
				LOG.warn(String.format("tag:current_account_item-fail,uid:%s,bid:%s", userDO.getId(), bidDO.getId()));
			}
		} else {
			alertService.warn(userDO.getId(), String.format("tag:frozen-fail,uid:%s,bid:%s,转复利冻结失败", userDO.getId(), bidDO.getId()));
			LOG.warn(String.format("tag:frozen-fail,uid:%s,bid:%s,转复利冻结失败", userDO.getId(), bidDO.getId()));
		}
	}

	/**
	 * 处理加息券
	 * 
	 * @param bidDO
	 */
	private void dealRateIncrCoupon(FinancingTargetBidDO bidDO, FinancingTargetDO financingTargetDO) {
		try {
			if (StringUtil.isEmpty(bidDO.getRateIncrCouponId())) {
				return;
			}
			RateIncrCouponDO couponDO = rateIncrCouponService.get(RateIncrCouponDO.class, bidDO.getRateIncrCouponId());
			// 检查加息券条件
			AssertUtil.assertTrue(rateIncrCouponService.testForConditions(bidDO, couponDO, financingTargetDO));

			// 使用该现金券
			couponDO.setFinancingTargetBidId(bidDO.getId());
			couponDO.setStatus("used");
			AssertUtil.assertTrue(rateIncrCouponService.updateSkipCheckOwner(couponDO));
			AssertUtil.assertTrue(financingTargetBidService.addRate(bidDO.getId(), couponDO.getRate()), "USE_RATE_INCR_COUPON_ERROR");

		} catch (MyException e) {
			LOG.error("处理现金券出错:" + e.getCode(), e);
		} catch (Exception e) {
			LOG.error("处理现金券出错", e);
		}

	}

	/**
	 * 处理现金券
	 * 
	 * @param bidDO
	 */
	private void dealCashCoupon(FinancingTargetBidDO bidDO, FinancingTargetDO financingTargetDO) {
		try {
			if (StringUtil.isEmpty(bidDO.getCashCouponId())) {
				return;
			}
			CashCouponDO cashCouponDO = cashCouponService.get(CashCouponDO.class, bidDO.getCashCouponId());
			// 检查现金券条件
			AssertUtil.assertTrue(cashCouponService.testForConditions(bidDO, cashCouponDO, financingTargetDO));

			// 使用该现金券
			cashCouponDO.setFinancingTargetBidId(bidDO.getId());
			cashCouponDO.setUsedAmount(cashCouponDO.getAmount());
			AssertUtil.assertTrue(cashCouponService.updateSkipCheckOwner(cashCouponDO));
			if(cashCouponDO.isAddProfit()){
				AssertUtil.assertTrue(financingTargetBidService.updateExpireSendProfit(bidDO.getId(), cashCouponDO.getAmount()),"UPDATE_EXPIRE_SEND_PROFIT_FAIL");
			}else{
				AssertUtil.assertTrue(financingTargetBidService.addCashCouponAmount(bidDO.getId(), cashCouponDO.getAmount()), "CASH_COUPON_AMOUNT_ERROR");
			}

		} catch (MyException e) {
			LOG.error("处理现金券出错:" + e.getCode(), e);
		} catch (Exception e) {
			LOG.error("处理现金券出错", e);
		}

	}

	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.applicationEventPublisher = applicationEventPublisher;
	}
	
	@Transactional
	@Override
	public boolean transferBidToCurrentAccount(String bid) {
		FinancingTargetBidDO bidDO = this.financingTargetBidService.get(FinancingTargetBidDO.class, bid);
		UserDO userDO = this.userService.get(UserDO.class, bidDO.getBidUserId());
		this.putIntoCurrentAccount(userDO, bidDO);
		return true;
	}
}
